package com.qen.encrypt.filter;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson2.JSONObject;
import com.qen.encrypt.exception.UrlNotFoundException;
import com.qen.encrypt.http.CustomServletRequest;
import com.qen.encrypt.http.DecryptHttpServletRequestWrapper;
import com.qen.encrypt.http.DecryptMultipartRequestWrapper;
import com.qen.encrypt.inter.ApiResponse;
import com.qen.encrypt.inter.Decrypt;
import com.qen.encrypt.inter.EncryptGroup;
import com.qen.encrypt.inter.NoSign;
import com.qen.encrypt.inter.ValidSign;
import com.qen.encrypt.properties.EncryptMapConfig;
import com.qen.encrypt.utils.ParamEncrypt;
import com.qen.encrypt.utils.SignCheck;
import com.qen.encrypt.utils.Utils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.QueryParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 解密过滤器
 * @author licz
 */
public class DecryptRequestFilter extends OncePerRequestFilter {
    private final Logger log = LoggerFactory.getLogger(getClass());
    private final EncryptMapConfig encryptMapConfig;

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;
    private UrlPathHelper urlPathHelper;
    @Autowired
    private MultipartResolver multipartResolver;

    public DecryptRequestFilter(EncryptMapConfig encryptMapConfig, UrlPathHelper urlPathHelper) {
        this.encryptMapConfig = encryptMapConfig;
        this.urlPathHelper = urlPathHelper;
    }

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // 手动解析请求路径并设置到请求属性中
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

        if (request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE) == null) {
            ServletRequestPathUtils.parseAndCache(request);
        }

        CustomServletRequest wrapper = null;
        if (multipartResolver.isMultipart(request)) {
            wrapper = new DecryptMultipartRequestWrapper(request, multipartResolver);
        } else {
            wrapper = new DecryptHttpServletRequestWrapper(request);
        }

        ParamEncrypt paramEncrypt = encryptMapConfig.getParamEncrypt();
        SignCheck signCheck = encryptMapConfig.getSignCheck();
        try {
            HandlerExecutionChain chain = requestMappingHandlerMapping.getHandler(wrapper);
            if (chain == null) {
                filterChain.doFilter(request, response);
                return;
            }
            Object handler = chain.getHandler();
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                // 获取控制器类和方法
                Class<?> controllerClass = handlerMethod.getBeanType();
                Method method = handlerMethod.getMethod();

                Set<String> encryptSet = new HashSet<>();
                Set<String> noSignParam = new HashSet<>();
                for (Parameter parameter : method.getParameters()) {
                    Class<?> type = parameter.getType();
                    if (parameter.isAnnotationPresent(Decrypt.class)) {
                        // 不是基础类型的话需要将实体中的每个参数均解密
//                    ClassUtil.isBasicType(type)
                        if (ClassUtil.getPackage(type).startsWith("com.qen")) {
                            for (Field field : ReflectUtil.getFields(type)) {
                                encryptSet.add(field.getName());
                            }
                        } else if (parameter.isAnnotationPresent(RequestParam.class)) {
                            encryptSet.add(parameter.getAnnotation(RequestParam.class).value());
                        } else if (parameter.isAnnotationPresent(QueryParam.class)) {
                            encryptSet.add(parameter.getAnnotation(QueryParam.class).value());
//                        } else if (parameter.isAnnotationPresent(PathVariable.class)) {
//                            encryptSet.add(parameter.getAnnotation(PathVariable.class).value());

                        } else {
                            encryptSet.add(parameter.getAnnotation(Decrypt.class).value());
                        }
                    } else if (parameter.isAnnotationPresent(RequestPart.class)) {
                        encryptSet.add(parameter.getAnnotation(RequestPart.class).value());
                    }

                    if (ClassUtil.getPackage(type).startsWith("com.qen")) {
                        for (Field field : ReflectUtil.getFields(type)) {
                            if (field.isAnnotationPresent(NoSign.class)) {
                                noSignParam.add(field.getName());
                            }
                        }
                    } else if (parameter.isAnnotationPresent(RequestParam.class) && parameter.isAnnotationPresent(NoSign.class)) {
                        noSignParam.add(parameter.getAnnotation(RequestParam.class).value());
                    } else if (parameter.isAnnotationPresent(QueryParam.class) && parameter.isAnnotationPresent(NoSign.class)) {
                        noSignParam.add(parameter.getAnnotation(QueryParam.class).value());
//                        } else if (parameter.isAnnotationPresent(PathVariable.class)) {
//                            encryptSet.add(parameter.getAnnotation(PathVariable.class).value());
                    } else if (parameter.isAnnotationPresent(RequestPart.class)) {
                        noSignParam.add(parameter.getAnnotation(RequestPart.class).value());
                    } else if (parameter.isAnnotationPresent(NoSign.class)) {
                        noSignParam.add(parameter.getAnnotation(NoSign.class).value());
                    }

                }
                boolean validFlag = false;
                if (method.isAnnotationPresent(ValidSign.class) || controllerClass.isAnnotationPresent(ValidSign.class)) {
                    validFlag = true;
                }

                String bodys = wrapper.getBodyString();
                // 当前这个请求 没有加密的参数，所以直接不走下面的部分了
                if (encryptSet.isEmpty() && !validFlag && StrUtil.isBlank(bodys)) {
                    filterChain.doFilter(wrapper, response);
                    return;
                }

                if (method.isAnnotationPresent(EncryptGroup.class)) {
                    String group = method.getAnnotation(EncryptGroup.class).value();
                    if (StrUtil.isNotBlank(group)) {
                        paramEncrypt = encryptMapConfig.getParamEncrypt(group);
                        signCheck = encryptMapConfig.getSignCheck(group);
                    }
                } else if (controllerClass.isAnnotationPresent(EncryptGroup.class)) {
                    String group = controllerClass.getAnnotation(EncryptGroup.class).value();
                    if (StrUtil.isNotBlank(group)) {
                        paramEncrypt = encryptMapConfig.getParamEncrypt(group);
                        signCheck = encryptMapConfig.getSignCheck(group);
                    }
                }
                try {
                    Map<String, String[]> params = wrapper.getParameterMap();
                    JSONObject param = new JSONObject();
                    for (Map.Entry<String, String[]> entry : params.entrySet()) {
                        String k = entry.getKey();
                        if (!noSignParam.contains(k)) {
                            param.put(k, entry.getValue()[0]);
                        }
                        if (encryptSet.contains(k)) {
                            String[] v = entry.getValue();
                            String[] vn = new String[v.length];
                            for (int i = 0; i < v.length; i++) {
                                vn[i] = paramEncrypt.priDecrypt(v[i]);
                            }
                            wrapper.addParameter(k, vn);
                        } else {
                            wrapper.addParameter(k, entry.getValue());
                        }
                    }
                    if (validFlag) {
                        String paramString = Utils.sortJsonAndBuildString(param);
                        if (StrUtil.isNotBlank(bodys)) {
                            if (StrUtil.isNotBlank(paramString)) {
                                paramString += "&POST_BODY=" + bodys;
                            } else {
                                paramString = "POST_BODY=" + bodys;
                            }
                        }
                        String sign = request.getHeader("Sign");
                        boolean flag = signCheck.verify(paramString, sign);
                        if (!flag) {
                            String pathWithinApplication = urlPathHelper.getPathWithinApplication(request);
                            log.error("签名校验失败，请求：{};参数：{}", pathWithinApplication, paramString);

                            JSONObject result = new JSONObject();
                            result.put("message", "签名校验失败");
                            result.put("code", 303);
                            result.put("msg", "签名校验失败");
                            result.put("result", false);
                            ApiResponse.responseJson(HttpStatus.HTTP_OK, result, response);
                            return;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();

                    JSONObject result = new JSONObject();
                    result.put("message", "参数错误");
                    result.put("code", 303);
                    result.put("msg", e.getMessage());
                    result.put("result", false);
                    ApiResponse.responseJson(HttpStatus.HTTP_OK, result, response);
                    return;
                }

            }
        } catch (UrlNotFoundException e) {
            e.printStackTrace();
            JSONObject result = new JSONObject();
            result.put("message", e.getMessage());
            result.put("code", e.getCode());
            result.put("msg", e.getMessage());
            result.put("result", false);
            ApiResponse.responseJson(HttpStatus.HTTP_NOT_FOUND, result, response);
            return;
        } catch (Exception e) {
            e.printStackTrace();

            JSONObject result = new JSONObject();
            result.put("message", "参数错误");
            result.put("code", 303);
            result.put("msg", e.getMessage());
            result.put("result", false);

            ApiResponse.responseJson(HttpStatus.HTTP_OK, result, response);
            return;
        }
        filterChain.doFilter(wrapper, response);
    }

    public EncryptMapConfig getEncryptMapConfig() {
        return encryptMapConfig;
    }

}
